<?php


namespace app\service\alone\Upload;


use app\service\alone\BaseService;
use app\service\alone\Upload\Traits\FragmentationTrait;
use think\facade\Cache;
use think\file\UploadedFile;

class FragmentationService extends BaseService
{
    use FragmentationTrait;

    // 缓存数据
    protected $data = [];

    /**
     * 生成上传文件 key
     *
     * @param string $name 文件名称
     * @param int $size 文件大小
     * @param int $totalSheet 总片数
     * @return array
     */
    public function generateUploadFileKey(string $name, int $size, int $totalSheet): array
    {
        list($dir, $detail, $realName) = [
            root_path('public' . DIRECTORY_SEPARATOR . 'storage'),
            DIRECTORY_SEPARATOR . 'sheet' . DIRECTORY_SEPARATOR . date('Y-m-d') . DIRECTORY_SEPARATOR,
            sha1(randomNum(uniqid() . rand(111, 99999)))
        ];

        // 缓存的数据
        $data = [
            'info' => [
                'name' => $name,
                'size' => $size,
                'totalSheet' => $totalSheet,
            ],

            'storage' => [
                'dir' => root_path('public' . DIRECTORY_SEPARATOR . 'storage'),
                'temp' => 'temp' . $detail . $realName . DIRECTORY_SEPARATOR,
                'synthesis' => 'resources' . $detail,
                'name' => $realName . '.' . pathinfo($name)['extension'],
            ],
        ];

        // 锁文件
        $data['lock'] = [
            'queue' => $data['storage']['dir'] . $data['storage']['temp'] . 'file_list.txt',
            'lock' => $data['storage']['dir'] . $data['storage']['temp'] . 'file_list.lock'
        ];

        // 判断文件夹是否存在
        if (!is_dir($path = $dir . $data['storage']['temp'])) mkdir($path, 0755, true);
        if (!is_dir($path = $dir . $data['storage']['synthesis'])) mkdir($path, 0755, true);

        // 判断锁队列文件是否存在
        $file = fopen($data['lock']['queue'], 'w');
        fclose($file);

        // 判断锁文件是否存在
        $file = fopen($data['lock']['lock'], 'w');
        fclose($file);

        // 缓存数据
        $key = sha1(uniqid() . randomNum(rand(111, 999999)));
        Cache::set($key, $data, 7200);

        return echoArr(200, '请求成功', ['key' => $key]);
    }

    /**
     * 上传文件
     *
     * @param string $key 文件key
     * @param string $md5 当前文件md5
     * @param int $index 当前文件片数位置
     * @param UploadedFile $file 上传的文件流
     * @return array
     */
    public function upload(string $key, string $md5, int $index, UploadedFile $file): array
    {
        // 文件信息
        if (!$this->data = Cache::get($key)) return echoArr(500, '文件key失效');

        // 验证文件完整性
        if ($md5 !== $file->md5()) return echoArr(500, '文件丢失，请重新上传当前文件块');

        // 文件名称
        $fileName = $index . '.yu';

        // 验证是否重新上传
        if (is_file($this->data['storage']['dir'] . $this->data['storage']['temp'] . $fileName)) return echoArr(500, '请勿重复上传文件块');

        try {
            // 存储文件
            $result = $this->storage($file, $fileName);
            if (200 != $result['code']) return $result;

            // 重新上传，则不加入文件队列
            $this->pushFileQueue($fileName);

            // 是否需要合并文件
            $isMerge = $this->verifyIsMergeFile($key);
        } catch (\Exception $exception) {
            return echoArr(500, '文件上传失败');
        }

        return echoArr(200, '请求成功', ['isMerge' => $isMerge, 'input' => $isMerge ? $this->data['storage']['synthesis'] . $this->data['storage']['name'] : '']);
    }

    /**
     * 重置文件块信息（上传文件块失败的情况下）
     *
     * @param string $key
     * @param array $indexList
     * @return array
     */
    public function reset(string $key, array $indexList): array
    {
        // 文件信息
        if (!$this->data = Cache::get($key)) return echoArr(500, '文件key失效');

        // 处理重置文件
        $indexList = array_map(function ($v) {
            return $v . '.yu';
        }, $indexList);

        // 重置队列内容
        $queueList = $this->getFileQueue();
        $file = fopen($this->data['lock']['queue'], 'w+');
        while ($fileName = array_shift($queueList)) {
            if (!in_array($fileName, $indexList)) {
                // 重新写入
                fwrite($file, $fileName . "\n");
            } else {
                // 删除需重新上传的文件块
                if (is_file($path = $this->data['storage']['dir'] . $this->data['storage']['temp'] . $fileName)) unlink($path);
            }
        }
        fclose($file);

        return echoArr(200, '重置文件块成功');
    }

    /**
     * 取消上传
     *
     * @param string $key
     * @return array
     */
    public function cancel(string $key): array
    {
        // 文件信息
        if (!$this->data = Cache::get($key)) return echoArr(500, '文件key失效');

        // 删除文件
        $this->deleteFileBlockRecording($this->getFileQueue(), $key);

        return echoArr(200, '操作成功');
    }
}